package com.aabte.auth.group.application;

import com.aabte.auth.account.application.AccountService;
import com.aabte.auth.account.domain.model.Account;
import com.aabte.auth.account.domain.model.AccountId;
import com.aabte.auth.account.domain.repository.AccountRepository;
import com.aabte.auth.group.domain.exception.GroupCannotDeleteException;
import com.aabte.auth.group.domain.exception.GroupNotExistException;
import com.aabte.auth.group.domain.model.Group;
import com.aabte.auth.group.domain.model.GroupId;
import com.aabte.auth.group.domain.model.GroupName;
import com.aabte.auth.group.domain.repository.GroupRepository;
import com.aabte.auth.role.application.RoleService;
import com.aabte.auth.role.domain.model.Role;
import com.aabte.auth.role.domain.model.RoleId;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Optional;

@Service
public class GroupService {

    @Resource
    private GroupRepository groupRepository;
    @Resource
    private AccountRepository accountRepository;
    @Resource
    private AccountService accountService;
    @Resource
    private RoleService roleService;

    @Transactional(rollbackFor = Exception.class)
    public GroupId create(GroupName groupName) {
        Group group = new Group(groupRepository.nextGroupId(), groupName);
        groupRepository.addOrUpdate(group);

        return group.getGroupId();
    }

    @Transactional(rollbackFor = Exception.class)
    public void disable(GroupId groupId) {
        Optional<Group> groupOptional = groupRepository.find(groupId);
        if (!groupOptional.isPresent()) {
            throw new GroupNotExistException("groupId: " + groupId.getId());
        }
        Group group = groupOptional.get();
        group.disable();
        groupRepository.remove(group);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean delete(GroupId groupId) {
        Optional<Group> groupOptional = groupRepository.find(groupId);
        if (!groupOptional.isPresent()) {
            throw new GroupNotExistException("groupId: " + groupId.getId());
        }

        Group group = groupOptional.get();
        if (canDelete(group)) {
            groupRepository.remove(group);
            return true;
        }
        throw new GroupCannotDeleteException("groupId: " + groupId.getId());
    }

    @Transactional(rollbackFor = Exception.class)
    public void addAccount(GroupId groupId, AccountId accountId) {
        Group group = group(groupId);
        if (group.containsAccount(accountId)) {
            return;
        }

        Account account = accountService.account(accountId);
        group.addAccount(account);
        groupRepository.addOrUpdate(group);
    }

    public void addRole(GroupId groupId, RoleId roleId) {
        Group group = group(groupId);
        if (group.containsRole(roleId)) {
            return;
        }

        Role role = roleService.role(roleId);
        group.addRole(role);
        groupRepository.addOrUpdate(group);
    }

    public Group group(GroupId groupId) {
        Optional<Group> groupOptional = groupRepository.find(groupId);
        if (groupOptional.isPresent()) {
            return groupOptional.get();
        }
        throw new GroupNotExistException("groupId: " + groupId.getId());
    }

    private boolean canDelete(Group group) {
        return CollectionUtils.isEmpty(group.getAccounts());
    }

    public Page<Group> groups(Pageable pageable) {
        return groupRepository.find(pageable);
    }
}
